<template>
    <TabScaffold :current="pageCurrent" :pageSize="pageSize" :total="total" :pageCount="pageData.length"
        :checkAll="checkAll" @update:current="onChangePage" @update:pageSize="onChangePageSize" @check-all="onCheckAll">
        <template #toolbar-left>
            <a-space>
                <a-button class="add-btn" @click="onAdd">新增</a-button>
                <a-button :disabled="!selectedRowKeys.length" danger @click="onBatchDelete">批量删除</a-button>
                <a-button @click="openImport">导入</a-button>
                <a-button :loading="exporting" @click="openExport">导出</a-button>
            </a-space>
        </template>

        <template #toolbar-right>
            <a-input-search v-model:value="keyword" :enter-button="true" allow-clear placeholder="输入搜索关键词"
                style="width: 340px" @search="onSearch" />
        </template>

        <div class="table-wrap">
            <a-table :columns="columns" :data-source="pageData" :loading="loading" :row-selection="rowSelection"
                bordered :pagination="false" row-key="id" size="middle" :scroll="{ x: 'max-content' }"
                :customRow="customRow">
                <template #url="{ text }">
                    <span class="url-text" :title="text">{{ text }}</span>
                </template>
                <template #channel="{ text }">
                    <span>{{ catMap[text] || text || '-' }}</span>
                </template>
                <template #status="{ record }">
                    <span :style="{ color: record.status === 'published' ? '#16a34a' : '#999' }">
                        {{ record.status === 'published' ? '启用' : '停用' }}
                    </span>
                </template>
                <template #actions="{ record }">
                    <a class="op-link" @click.stop="onEdit(record)">编辑</a>
                    <span class="op-sep">|</span>
                    <a class="op-link danger" @click.stop="onDelete(record)">删除</a>
                    <span class="op-sep">|</span>
                    <a class="op-link" :class="{ disabled: record.status !== 'published' }"
                        @click.stop="record.status === 'published' && onDisable(record)">停用</a>
                    <span class="op-sep">|</span>
                    <a class="op-link" :class="{ disabled: record.status !== 'draft' }"
                        @click.stop="record.status === 'draft' && onEnable(record)">启用</a>
                </template>
            </a-table>
        </div>

        <ImportModal v-model="importVisible" title="导入链接" tip="支持 CSV 或 JSON（UTF-8）。" @import="doImport" />
        <ExportModal v-model="exportVisible" :loading="exporting" :format="exportFormat" title="导出链接"
            @update:format="(v) => (exportFormat = v)" @confirm="doExport" />
    </TabScaffold>
</template>

<script lang="ts" setup>
import { ref, computed, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { Modal, message } from 'ant-design-vue';
import type { Key } from 'ant-design-vue/es/table/interface';
import TabScaffold from '@/components/TabScaffold.vue';
import ImportModal from '@/components/ImportModal.vue';
import ExportModal from '@/components/ExportModal.vue';

import {
    getLinkInsertList,
    deleteLinkInsert,
    deleteLinkInserts,
    enableLinkInsert,
    disableLinkInsert,
    exportLinkInsertsJSON,
    getVerticalCategories,
    importLinkInserts,
    type LinkInsertItem,
    type VerticalCategory,
} from '@/api/modules/linkInsert.api';

type RowItem = {
    id: string | number
    query: string
    title: string
    url: string
    channel: string
    summary: string
    location: string
    status: 'published' | 'draft'
}

const route = useRoute();
const router = useRouter();
const TAB_KEY = 'linkInsertion';

const loading = ref(false);
const keyword = ref('');
const pageCurrent = ref(1);
const pageSize = ref(10);
const total = ref(0);
const selectedRowKeys = ref<Key[]>([]);
const pageData = ref<RowItem[]>([]);

/** 频道映射 */
const catMap = ref<Record<string, string>>({});
async function loadCats() {
    const cats = await getVerticalCategories();
    const map: Record<string, string> = {};
    (cats as VerticalCategory[]).forEach(c => { map[c.value] = c.query; });
    catMap.value = map;
}

const columns = [
    { title: '查询关键词', dataIndex: 'query', key: 'query', width: 160 },
    { title: '标题', dataIndex: 'title', key: 'title', width: 220, ellipsis: true },
    { title: 'URL地址', dataIndex: 'url', key: 'url', slots: { customRender: 'url' }, width: 240, ellipsis: true },
    { title: '频道', dataIndex: 'channel', key: 'channel', slots: { customRender: 'channel' }, width: 120 },
    { title: '摘要', dataIndex: 'summary', key: 'summary', width: 160, ellipsis: true },
    { title: '地理位置', dataIndex: 'location', key: 'location', width: 140 },
    { title: '状态', key: 'status', slots: { customRender: 'status' }, width: 100 },
    { title: '操作', key: 'actions', slots: { customRender: 'actions' }, width: 220, fixed: 'right' },
];

const rowSelection = computed(() => ({
    selectedRowKeys: selectedRowKeys.value,
    onChange: (keys: Key[]) => (selectedRowKeys.value = keys),
}));
const currentPageKeys = computed<Key[]>(() => pageData.value.map((r) => r.id));
const checkAll = computed(
    () => currentPageKeys.value.length > 0 && currentPageKeys.value.every((k) => selectedRowKeys.value.includes(k)),
);
function onCheckAll(e: any) {
    const checked = e?.target?.checked;
    selectedRowKeys.value = checked
        ? Array.from(new Set([...selectedRowKeys.value, ...currentPageKeys.value]))
        : selectedRowKeys.value.filter((k) => !currentPageKeys.value.includes(k));
}

/** 列表 */
async function fetchList() {
    loading.value = true;
    try {
        const { list, total: t } = await getLinkInsertList({
            page: pageCurrent.value,
            pageSize: pageSize.value,
            keyword: keyword.value,
            sort: 'id',
            excludeStatus: 'archived',
        });

        pageData.value = (list || []).map((it: LinkInsertItem) => ([
            it.id,
            it.query ?? '',
            it.title ?? '',
            it.page_url ?? '',
            it.sence ?? '',
            it.summary ?? '',
            it.position ?? '',
            (it.status === 'published' ? 'published' : 'draft') as 'published' | 'draft',
        ])).map(([id, query, title, url, channel, summary, location, status]) => ({
            id: id as any,
            query: query as string,
            title: title as string,
            url: url as string,
            channel: channel as string,
            summary: summary as string,
            location: location as string,
            status: status as 'published' | 'draft',
        }));
        total.value = t || 0;
        selectedRowKeys.value = [];
    } finally {
        loading.value = false;
    }
}

/** 启用/停用/删除 */
async function onEnable(row: RowItem) { await enableLinkInsert(row.id); row.status = 'published'; message.success('已启用'); }
async function onDisable(row: RowItem) { await disableLinkInsert(row.id); row.status = 'draft'; message.success('已停用'); }
function onDelete(row: RowItem) {
    Modal.confirm({
        title: '确认删除该记录？',
        content: `将删除：${row.title || row.query || row.url}`,
        okText: '删除', okType: 'danger', cancelText: '取消',
        async onOk() { await deleteLinkInsert(row.id); message.success('删除成功'); if (pageData.value.length === 1 && pageCurrent.value > 1) pageCurrent.value -= 1; await fetchList(); },
    });
}
async function onBatchDelete() {
    if (!selectedRowKeys.value.length) return;
    Modal.confirm({
        title: '确认批量删除选中项？', okText: '删除', okType: 'danger', cancelText: '取消',
        async onOk() { await deleteLinkInserts(selectedRowKeys.value as Array<string | number>); message.success('删除成功'); if (pageData.value.length === selectedRowKeys.value.length && pageCurrent.value > 1) pageCurrent.value -= 1; await fetchList(); },
    });
}

/** 新增 / 编辑 / 详情 跳转 */
function backQueryForTab() { return { backTo: 'tab', from: route.path, tab: TAB_KEY }; }
function onAdd() { router.push({ path: '/linkInsertionPage', query: { ...backQueryForTab(), mode: 'create' } }); }
function onEdit(row: RowItem) {
    router.push({
        path: '/linkInsertionPage',
        query: { ...backQueryForTab(), mode: 'edit', id: String(row.id), row: encodeURIComponent(JSON.stringify(row)) },
    });
}
function onRowClick(row: RowItem) {
    router.push({
        path: '/linkInsertionInfo',
        query: {
            ...backQueryForTab(),
            id: String(row.id),
            row: encodeURIComponent(JSON.stringify({
                id: row.id,
                query: row.query,
                title: row.title,
                page_url: row.url,
                sence: row.channel,
                position: row.location,
                summary: row.summary,
                status: row.status,
            })),
        },
    });
}
function customRow(row: RowItem) { return { onClick: () => onRowClick(row), class: 'row-clickable' }; }

/** 搜索/分页 */
function onSearch() { pageCurrent.value = 1; fetchList(); }
function onChangePage(v: number) { pageCurrent.value = v; fetchList(); }
function onChangePageSize(v: number) { pageSize.value = v; pageCurrent.value = 1; fetchList(); }

/** 导入/导出 */
const importVisible = ref(false);
const exportVisible = ref(false);
const exporting = ref(false);
const exportFormat = ref<'csv' | 'json'>('csv');

function openImport() { importVisible.value = true; }
function openExport() { exportVisible.value = true; }

/** 导入 */
async function doImport(file: File) {
    if (!file) { message.warning('请选择要导入的文件'); return; }
    const key = 'link-insert-importing';
    message.loading({ content: '正在导入…', key, duration: 0 });
    try {
        const name = (file as any).name || '';
        const ext = name.split('.').pop()?.toLowerCase();
        const format = ext === 'json' ? 'json' : 'csv';
        await importLinkInserts(file, { filename: name, format });
        message.success({ content: '导入成功', key });
        importVisible.value = false;
        selectedRowKeys.value = [];
        pageCurrent.value = 1;
        await fetchList();
    } catch (e: any) {
        const errMsg =
            e?.response?.errors?.[0]?.message ||
            e?.message ||
            '导入失败';
        message.error({ content: errMsg, key });
    }
}

/** === 导出 === */
async function doExport(fmt?: 'csv' | 'json') {
    const format = fmt || exportFormat.value || 'csv';
    exporting.value = true;
    try {
        const ids = (selectedRowKeys.value as Array<string | number>) ?? [];
        const hasSelection = ids.length > 0;

        const blob = await exportLinkInsertsJSON({
            ids: hasSelection ? ids : undefined,
            keyword: hasSelection ? '' : keyword.value,
            excludeStatus: 'archived',
        });

        if (format === 'json') {
            downloadBlob(blob, buildFileName('json'), 'application/json;charset=utf-8');
        } else {
            const text = await blobToText(blob);
            let rows: any[] = [];
            try { rows = JSON.parse(text) as any[]; } catch { rows = []; }
            const csv = toCSV(rows, [
                'query', 'title', 'page_url', 'sence', 'position', 'summary', 'status', 'date_created',
            ]);
            downloadBlob(new Blob([csv], { type: 'text/csv;charset=utf-8' }), buildFileName('csv'), 'text/csv;charset=utf-8');
        }

        exportVisible.value = false;
        message.success('导出成功');
    } catch (e: any) {
        message.error(e?.message || '导出失败');
    } finally {
        exporting.value = false;
    }
}

/** ===== 工具 ===== */
function blobToText(b: Blob): Promise<string> {
    return new Promise((resolve, reject) => {
        const fr = new FileReader();
        fr.onload = () => resolve(String(fr.result || ''));
        fr.onerror = reject;
        fr.readAsText(b);
    });
}
function csvEscape(v: any) {
    const s = String(v ?? '');
    return /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
}
function toCSV(rows: Array<Record<string, any>>, columns: string[]): string {
    const header = columns.join(',');
    const lines = (rows || []).map(r => columns.map(c => csvEscape(r?.[c])).join(','));
    return '\uFEFF' + [header, ...lines].join('\n');
}
function downloadBlob(blob: Blob, filename: string, _mime: string) {
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url; a.download = filename; a.click();
    URL.revokeObjectURL(url);
}
function buildFileName(ext: 'csv' | 'json') {
    const d = new Date();
    const pad = (n: number) => String(n).padStart(2, '0');
    const ts = `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}_${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
    return `link_insert_${ts}.${ext}`;
}

function normalizeUrl(u: string) { if (!/^https?:\/\//i.test(u)) return `http://${u}`; return u; }

onMounted(async () => { await loadCats(); await fetchList(); });
</script>

<style lang="less" scoped>
.table-wrap {
    width: 100%;
    overflow-x: auto;
    overflow-y: hidden;
}

.add-btn {
    background: #fff;
    color: #1677ff;
    border: 1px solid #1677ff;
}

.add-btn:hover,
.add-btn:focus {
    background: #fff;
    color: #0958d9;
    border-color: #0958d9;
}

.url-text {
    display: inline-block;
    max-width: 100%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    color: #333;
}

:deep(.row-clickable) {
    cursor: pointer;
}

:deep(.row-clickable:hover > td) {
    background: #fafafa !important;
}

@media (max-width:768px) {
    :deep(.ant-input-search) {
        width: 100% !important;
    }
}

.op-link {
    color: #1677ff;
    cursor: pointer;
}

.op-link.danger {
    color: #ff4d4f;
}

.op-link.disabled {
    color: #bfbfbf;
    cursor: not-allowed;
    pointer-events: none;
}

.op-sep {
    color: #bfbfbf;
    margin: 0 6px;
}

:deep(.ant-table) {

    .ant-table-thead>tr>th,
    .ant-table-tbody>tr>td {
        padding: 8px 12px;
        height: 40px;
    }

    .ant-table-tbody>tr>td {
        color: #333;
    }
}
</style>
